#include "scene3d.h"
#include "spacecol3d.h"
#include <assert.h>
#include "bsave.h"
extern "C"{
    extern void APIENTRY glBindRenderbufferEXT (GLenum, GLuint);
    extern void APIENTRY glDeleteRenderbuffersEXT (GLsizei, const GLuint *);
    extern void APIENTRY glGenRenderbuffersEXT (GLsizei, GLuint *);
    extern void APIENTRY glActiveTexture (GLenum);
    extern void APIENTRY glFramebufferTexture2DEXT (GLenum, GLenum, GLenum, GLuint, GLint);
    extern void APIENTRY glRenderbufferStorageEXT (GLenum, GLenum, GLsizei, GLsizei);
    extern void APIENTRY glFramebufferRenderbufferEXT (GLenum, GLenum, GLenum, GLuint);
}
Scene3D::Scene3D(Renderer * rend)
{
    drawer = new TreeRender(rend);

    shadowGrid3D = new Shadow3D();
    selected_tree = -1;
    shadow = true;
    drawAlgo = true;

    BPR = BPR3D;
    BPRSQ = BPR*BPR;
    numBlock = BPRSQ*BPR;
    m_blocks = new Block[numBlock];
    generateMarkers();
    m_addMultiMeta = true;
    m_appicalDepth = 1;
    m_useBH = true;
    render = rend;
    count = 0;
}


Scene3D::~Scene3D(){
    delete drawer;
    delete shadowGrid3D;
    //delete tree0;

    //need to fix this now that all the trees are stored in space col
    foreach(TreeGen3D * atree, forest){
        delete atree;
    }

}

void Scene3D::generateMarkers()
{
    double size = (double) PLANE_SIZE/BPR;

    for (int i = 0; i < numBlock; i++)
    {
        Block * currentBlock = &m_blocks[i];
        currentBlock->depth = i / BPRSQ;
        int index2d = i % BPRSQ;
        currentBlock->col = (int) index2d % BPR;
        currentBlock->row = (int) index2d / BPR;
        currentBlock->updateSize(size, float3 (currentBlock->col * size, currentBlock->row * size, currentBlock->depth*size));
        for (int j = 0; j < currentBlock->numMarkers; j++)
        {
            double random = ((double) rand()) / (double) RAND_MAX;
            currentBlock->m_markers.push_back( (random*size) + currentBlock->startCoord.x); // x coord

            double random2 = ((double) rand()) / (double) RAND_MAX;
            double range2 = size;
            currentBlock->m_markers.push_back((random2*size) + currentBlock->startCoord.y); // y coord

            double random3 = ((double) rand()) / (double) RAND_MAX;
            currentBlock->m_markers.push_back((random3*size) + currentBlock->startCoord.z); // y coord

            currentBlock->m_markers.push_back( -1); // does not belong to any bud;
            currentBlock->m_markers.push_back( -1); // no distance calculated yet;
            /*if (currentBlock->m_markers.at(j*5 + 0) < 0 || currentBlock->m_markers.at(j*5 + 1) < 0)
            {
                cout << "smaller than 0:" << currentBlock->col << "//" << currentBlock->row << endl;
               // cout << "value:" << currentBlock->m_markers[j*5 + 0] << "//" << currentBlock->m_markers[j*5 + 1] << endl;
                cout << "random:" << random << "//" << random2 << endl;
                cout << "range:" << range << "//" << range2 << endl;
                cout << "startCoord:" << currentBlock->startCoord << endl;
                cout << "size:" << size << endl;
                cout << endl;
            }*/
        }
    }
}

void Scene3D::drawMarkers()
{
    GLUquadric * mQuadric = gluNewQuadric();
    for (int k = 0; k < numBlock; k++)
    {
        Block * currentBlock = &m_blocks[k];
        for (int j = 0; j < currentBlock->m_markers.size()/5.0; j++)
        {
            if(currentBlock->m_markers.at(j*5 +3)< -1.9 && currentBlock->m_markers.at(j*5 +4)<-1.9)
            {
                glColor3f(0.7, 0.7, 0.7);
                glPushMatrix();
                glTranslatef(currentBlock->m_markers.at(j*5 + 0), currentBlock->m_markers.at(j*5 + 1),currentBlock->m_markers.at(j*5 + 2));
               /* glBegin(GL_QUADS);
                    glVertex3f(-.5,-.5,0);
                    glVertex3f(.5,-.5,0);
                    glVertex3f(.5,.5,0);
                    glVertex3f(-.5,.5,0);
                glEnd();*/
                //gluDisk(mQuadric, 0, 2.0/2.0, 10, 10);
                gluSphere(mQuadric,0.5,10,10);
                glPopMatrix();
            }

            else if(currentBlock->m_markers.at(j*5 +3)> 0)
            {
                glColor3f(0, 0.0, 1.0);
                glPushMatrix();
                glTranslatef(currentBlock->m_markers.at(j*5 + 0), currentBlock->m_markers.at(j*5 + 1),currentBlock->m_markers.at(j*5 + 2));
               /* glBegin(GL_QUADS);
                    glVertex3f(-.5,-.5,0);
                    glVertex3f(.5,-.5,0);
                    glVertex3f(.5,.5,0);
                    glVertex3f(-.5,.5,0);
                glEnd();*/
                //gluDisk(mQuadric, 0, 2.0/2.0, 10, 10);
                gluSphere(mQuadric,2.0,10,10);
                glPopMatrix();
            }

        }
    }
}

void Scene3D::addMarkers(double x, double y, double z)
{
    //is this right??
    double size = PLANE_SIZE/BPR;
    int col = (int)x/size;
    int row = (int)y/size;
    int depth = (int)z/size;
    int index = depth*BPRSQ +  row*BPR + col;
    if(x> PLANE_SIZE || y> PLANE_SIZE  || z> PLANE_SIZE){
        cout<<"indexing too high, returning"<<endl;
        return;
    }
    else if(x<0 || y<0 || z<0){
        cout<<"indexing too low, returning"<<endl;
        return;
    }
    m_blocks[index].m_markers.push_back(x);
    m_blocks[index].m_markers.push_back(y);
    m_blocks[index].m_markers.push_back(z);
    m_blocks[index].m_markers.push_back(-1);
    m_blocks[index].m_markers.push_back(-1);
}

void Scene3D::draw(bool id_mode){
    glMatrixMode(GL_MODELVIEW);

   // drawMarkers();
    //shadowGrid3D->draw();
    if (id_mode){
        for (int i = 0; i < spaceColForest.size(); i++)
        {
            //drawer->drawTree(forest[i]);
            // DIEM: space colonization tree:
            SpaceCol3D * sp3dtree = (SpaceCol3D*) spaceColForest[i];
            drawer->drawTree((TreeGen3D*)sp3dtree->m_tree, id_mode, i);
        }
    }
    else{
       //foreach(TreeGen3D *atree, forest){
            //glColor3f(1.0, 1.0, 1.0);
            //drawer->drawTree(atree);
        //}
        // DIEM: space colonization tree:
        for (int i = 0; i < spaceColForest.size(); i++)
        {
                glPushAttrib(GL_ALL_ATTRIB_BITS);
            glColor3f(1.0, 1.0, 1.0);
                        SpaceCol3D * sp3dtree = (SpaceCol3D*) spaceColForest[i];
            drawer->drawTree((TreeGen3D*) sp3dtree->m_tree, id_mode, i);
            glPopAttrib();
        }
        if(drawAlgo){
            if(shadow){
                //draw shadow grid, this is currently messed up.
            }
            /*else{
               this->drawMarkers();
            }*/
        }
    }

}

void Scene3D::drawPlane(float3 center,float angle, float halfsize)
{
    float smallx = center.x - halfsize;
    float bigx = center.x + halfsize;
    float smally = center.y - halfsize;
    float bigy = center.y + halfsize;
    float smallz = center.z;
    glBegin(GL_LINES);
    glVertex3f(smallx, smally, smallz);
    glVertex3f(bigx, smally, smallz);

    glVertex3f(bigx, smally, smallz);
    glVertex3f(bigx, bigy ,smallz );

    glVertex3f(bigx, bigy ,smallz );
    glVertex3f(smallx,bigy ,smallz );

    glVertex3f(smallx,bigy ,smallz );
    glVertex3f(smallx, smally, smallz);
    glEnd( );
}

void Scene3D::iterate(){
    if(shadow){
        shadowGrid3D->clearGrid();
        foreach(SpaceCol3D * sp3dtree, spaceColForest){
            shadowGrid3D->populateGrid(((TreeGen3D*)sp3dtree->m_tree)->treeRoot);
        }
    }
    if (selected_tree >= 0 && selected_tree <= 254){
        //TreeGen3D * aTree = forest[selected_tree];
        //aTree->iterateShadowB(shadowGrid3D);

        if(shadow){
            SpaceCol3D * sp3dtree = spaceColForest[selected_tree];
            ((TreeGen3D*)sp3dtree->m_tree)->iterateShadowB(shadowGrid3D);
        }
        else{
        //DIEM: space colonization tree
            SpaceCol3D * sp3dtree = spaceColForest[selected_tree];
            ((TreeGen3D*)sp3dtree->m_tree)->iterateSpaceColD(sp3dtree);
        }
    }

  count ++;
    // testing only.
    /*if (count == 3)
    {
        cout << "Count: " << count << endl;
        saveScene("", "scene1.txt",drawer->render->mCamera);
        loadScene("scene1.txt",drawer->render->mCamera);
        cout << "size:" << spaceColForest.size() << endl;
        count = 0;
    }*/
}

void Scene3D::clear(){
    if (selected_tree >= 0 && selected_tree <= 254){
        int pre_size_test = spaceColForest.size();
        //TreeGen3D * aTree = forest[selected_tree];
        forest[selected_tree]->freeTree(forest[selected_tree]->treeRoot);
        forest.erase(forest.begin()+selected_tree);


        //DIEM: space colonization tree
         SpaceCol3D * sp3dtree = spaceColForest[selected_tree];
         spaceColForest.erase(spaceColForest.begin()+selected_tree);
//NEED TO FIX:
         //sp3dtree->clearTree();

        index_count--;
        selected_tree = -1;
        assert(spaceColForest.size() == pre_size_test-1);
    }
}

void Scene3D::addTree(float2 position){
    assert(index_count >= 0 && index_count <= 254);
    //TreeGen3D * aTree =new TreeGen3D();
    //aTree->genTree(position);
    //aTree->iterateShadowB(shadowGrid3D);
    //forest.push_back(aTree);

    //DIEM: space colonization tree
    SpaceCol3D * sp3dtree = new SpaceCol3D(render);
     sp3dtree->initialize(position);
     sp3dtree->m_blocks = m_blocks; // assign the blocks of markers of the current scene to each tree
    ((TreeGen3D*)sp3dtree->m_tree)->m_addMultMeta = m_addMultiMeta;
    ((TreeGen3D*)sp3dtree->m_tree)->m_apicalDepth = m_appicalDepth;
    ((TreeGen3D*)sp3dtree->m_tree)->m_useBH = m_useBH;
    //((TreeGen3D*)sp3dtree->m_tree)->iterateShadowB(shadowGrid3D);

    spaceColForest.push_back(sp3dtree);
    forest.push_back(((TreeGen3D*)sp3dtree->m_tree));

    selected_tree = index_count;
    index_count++;
}

void Scene3D::addMultipleMetamers(bool b){
    foreach(SpaceCol3D * sp3dtree, spaceColForest){
        ((TreeGen3D*)sp3dtree->m_tree)->m_addMultMeta = b;
    }
    m_addMultiMeta = b;
}

void Scene3D::appicalControl(int b){
    foreach(SpaceCol3D * sp3dtree, spaceColForest){
        ((TreeGen3D*)sp3dtree->m_tree)->m_apicalDepth = b;
    }
    m_appicalDepth = b;
}

void Scene3D::useBH(bool b){
    foreach(SpaceCol3D * sp3dtree, spaceColForest){
        ((TreeGen3D*)sp3dtree->m_tree)->m_useBH = b;
    }
    m_useBH = b;
}

void Scene3D::iterateAll(){
    if(shadow){
        shadowGrid3D->clearGrid();
        foreach(SpaceCol3D * sp3dtree, spaceColForest){
            shadowGrid3D->populateGrid(((TreeGen3D*)sp3dtree->m_tree)->treeRoot);
        }
        foreach(SpaceCol3D * sp3dtree, spaceColForest){
            ((TreeGen3D*)sp3dtree->m_tree)->iterateShadowB(shadowGrid3D);;
        }
    }
    else{
    //DIEM: space colonization tree
        foreach(SpaceCol3D * sp3dtree, spaceColForest){
            ((TreeGen3D*)sp3dtree->m_tree)->iterateSpaceColD(sp3dtree);
        }
    }

}


bool Scene3D::bsave(QString filename, Camera *cam){
    QFile exists;
    if(exists.exists(filename)){
        cout<<"I will not save over an existing file"<<endl;
        return false;
    }

    for(int  i = 0; i< forest.size(); i++){
        TreeGen3D *tree = this->forest.at(i);
        SaveB(tree, i, filename, cam);
    }
    return true;
}

bool Scene3D::bload(QString filename, Camera *cam){
    assert(index_count >= 0 && index_count <= 254);
    QFile ex;
    if(!ex.exists(filename)){
        cout<<filename.toStdString()<<" does not exist"<<endl;
        return false;
    }
    //DIEM: space colonization tree
     // assign the blocks of markers of the current scene to each tree
     bool load = true, keeploading = true;
     int idx = 0;
     while(keeploading && load){
         TreeGen3D *tree = LoadTree(filename,load, keeploading, idx);
         if(load){
             SpaceCol3D * sp3dtree = new SpaceCol3D(render);
             sp3dtree->initialize(float2(0,0));
             sp3dtree->m_blocks = m_blocks;
             sp3dtree->updateTree(tree);;
             ((TreeGen3D*)sp3dtree->m_tree)->m_addMultMeta = m_addMultiMeta;
             ((TreeGen3D*)sp3dtree->m_tree)->m_apicalDepth = m_appicalDepth;
             ((TreeGen3D*)sp3dtree->m_tree)->m_useBH = m_useBH;
             ((TreeGen3D*)sp3dtree->m_tree)->treeRoot = tree->treeRoot;
             ((TreeGen3D*)sp3dtree->m_tree)->render = render;
             ((TreeGen3D*)sp3dtree->m_tree)->calculateBranchDiameter(((TreeGen3D*)sp3dtree->m_tree)->treeRoot);

             spaceColForest.push_back(sp3dtree);
             forest.push_back(((TreeGen3D*)sp3dtree->m_tree));

             selected_tree = index_count;
             index_count++;
             delete ((TreeGen3D*)sp3dtree->m_tree)->m_mesh;
             ((TreeGen3D*)sp3dtree->m_tree)->m_mesh = new TreeMesh((TreeGen3D*)sp3dtree->m_tree,render);
             idx++;
         }
         else{
             cout<<"LOAD FAILED"<<endl;
         }
     }
     QFile *in = new QFile(filename);
     in->open(QIODevice::ReadOnly | QIODevice::Text);
     QTextStream reader(in);
     QString next = reader.readLine();
     if(next.at(0).toAscii() == 'c'){
         QStringList split = next.split(" ");
         cam->eye = float3(split.at(1).toFloat(), split.at(2).toFloat(), split.at(3).toFloat());
         cam->center = float3(split.at(4).toFloat(), split.at(5).toFloat(), split.at(6).toFloat());
         cam->up = float3(split.at(7).toFloat(), split.at(8).toFloat(), split.at(9).toFloat());
         cam->_angle = split.at(10).toFloat();
         cam->_near = split.at(11).toFloat();
         cam->_far = split.at(12).toFloat();
         cam->nUp = float3(split.at(13).toFloat(), split.at(14).toFloat(), split.at(15).toFloat());
         cam->nCross = float3(split.at(16).toFloat(), split.at(17).toFloat(), split.at(18).toFloat());
         cam->nLook = float3(split.at(19).toFloat(), split.at(20).toFloat(), split.at(21).toFloat());
     }
     in->close();
     delete in;

     return load;

}




bool Scene3D::saveScene( QString filename, Camera * camera)
{
   // QString folder = path;
    //path.append(filename);
    cout << "Scene name:" << filename.toStdString() << endl;
    QFile * mout = new QFile(filename);
     if (!mout->open(QIODevice::WriteOnly | QIODevice::Text)) return false;
    QTextStream out(mout);

    // first line: camera info
    out << camera->eye.x << " ";
    out << camera->eye.y << " ";
    out << camera->eye.z << " ";
    out << camera->center.x << " ";
    out << camera->center.y << " ";
    out << camera->center.z << " ";
    out << camera->up.x << " ";
    out << camera->up.y << " ";
    out << camera->up.z << " ";
    out << camera->_angle << " ";
    out << camera->_near << " ";
    out << camera->_far << " ";
    out << camera->nUp.x << " ";
    out << camera->nUp.y << " ";
    out << camera->nUp.z << " ";
    out << camera->nCross.x << " ";
    out << camera->nCross.y << " ";
    out << camera->nCross.z << " ";
    out << camera->nLook.x << " ";
    out << camera->nLook.y << " ";
    out << camera->nLook.z << " ";
    out << endl;

    int l = spaceColForest.size();
    // 2nd line: num of trees:
    out << l << " " << endl;

    // from 3rd line: path & filename of trees:
    filename.append(".");
    for (int i = 0; i < l; i++)
    {
        QString treename = filename;
        treename.append("tree");
        QString num = treename.number(i);
        treename.append(num);
        treename.append(".txt");
        cout << "treename:" << treename.toStdString() << endl;
        out << treename << endl;
         SpaceCol3D * sp3dtree = spaceColForest.at(i);
         sp3dtree->m_tree->saveTreeToFile(treename);
    }
    mout->close();
    return true;
}

bool Scene3D::loadScene( QString filename, Camera * camera)
{
    QFile * min = new QFile(filename);
    if (!min->open(QIODevice::ReadOnly | QIODevice::Text))
        return false;
    QTextStream in(min);

    QString line = in.readLine();
    QStringList firstLine = line.split(" ");

    spaceColForest.clear();
    forest.clear();

        camera->eye.x = ((QString)firstLine.at(0)).toFloat();
        camera->eye.y = ((QString)firstLine.at(1)).toFloat();
        camera->eye.z = ((QString)firstLine.at(2)).toFloat();
        camera->center.x = ((QString)firstLine.at(3)).toFloat();
        camera->center.y = ((QString)firstLine.at(4)).toFloat();
        camera->center.z = ((QString)firstLine.at(5)).toFloat();
        camera->up.x = ((QString)firstLine.at(6)).toFloat();
        camera->up.y = ((QString)firstLine.at(7)).toFloat();
        camera->up.z = ((QString)firstLine.at(8)).toFloat();
        camera->_angle = ((QString)firstLine.at(9)).toFloat();
        camera->_near = ((QString)firstLine.at(10)).toFloat();
        camera->_far = ((QString)firstLine.at(11)).toFloat();
        camera->nUp.x = ((QString)firstLine.at(12)).toFloat();
        camera->nUp.y = ((QString)firstLine.at(13)).toFloat();
        camera->nUp.z = ((QString)firstLine.at(14)).toFloat();
        camera->nCross.x = ((QString)firstLine.at(15)).toFloat();
        camera->nCross.y = ((QString)firstLine.at(16)).toFloat();
        camera->nCross.z = ((QString)firstLine.at(17)).toFloat();
        camera->nLook.x = ((QString)firstLine.at(18)).toFloat();
        camera->nLook.y = ((QString)firstLine.at(19)).toFloat();
        camera->nLook.z = ((QString)firstLine.at(20)).toFloat();

        line = in.readLine();
        int size = line.toInt();
        cout << "Num trees: " << size << endl;
        //TreeGen3D temp;
        for (int i = 0; i < size; i ++)
        {
            line = in.readLine();
            SpaceCol3D * sp3dtree = new SpaceCol3D(render);
            sp3dtree->m_tree = new TreeGen3D(render);
            TreeGen3D * tree =  ((TreeGen3D*)sp3dtree->m_tree)->loadTreeFromFile(line);

            if (tree == NULL)
                return false;
            sp3dtree->updateTree(tree);
            sp3dtree->m_blocks = m_blocks; // assign the blocks of markers of the current scene to each tree
           ((TreeGen3D*)sp3dtree->m_tree)->m_addMultMeta = m_addMultiMeta;
           ((TreeGen3D*)sp3dtree->m_tree)->m_apicalDepth = m_appicalDepth;
           ((TreeGen3D*)sp3dtree->m_tree)->m_useBH = m_useBH;
           cout << "3" << endl;
           //((TreeGen3D*)sp3dtree->m_tree)->iterateShadowB(shadowGrid3D);
           spaceColForest.push_back(sp3dtree);
           forest.push_back(((TreeGen3D*)sp3dtree->m_tree));
           delete ((TreeGen3D*)sp3dtree->m_tree)->m_mesh;
           ((TreeGen3D*)sp3dtree->m_tree)->m_mesh = new TreeMesh((TreeGen3D*)sp3dtree->m_tree,render);
           //cout << "SIZE: " << forest.size() << endl;

           //cout << index_count << endl;
           //selected_tree = index_count;
           //index_count++;

        }


        min->close();

        return true;
}
void Scene3D::updateSelectedTreeBark(){
    if (selected_tree != -1){
        TreeGen3D *curTree = forest[selected_tree];
        treeBarkHelper(&curTree->barkGenerator, curTree->treeRoot);

    }
}

void Scene3D::treeBarkHelper(BarkGen * bgen, Bud *curBud){
    while (curBud->the_bark.age < curBud->season){
        bgen->ageBark(curBud->the_bark);
    }
    bgen->barkToTexture(curBud->the_bark);
    if (curBud->aux){
        treeBarkHelper(bgen, curBud->aux);
    }
    if (curBud->term){
        treeBarkHelper(bgen, curBud->term);
    }
}
